package com.jier.aop;

import com.jier.annotation.ValidateField;
import com.jier.annotation.ValidateGroup;
import com.jier.exception.ParamException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;

/**
 *
 * @author jie_r
 * @date 2017/3/28
 */
@Aspect
public abstract class ValidateAspectHandel {
    public Object[] args = null;

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void init() {
    }

    /**
     * 使用AOP对使用了ValidateGroup的方法进行代理校验
     *
     * @throws Throwable
     */
    @Around(value = "init()&&@annotation(requestMapping)&&@annotation(validateGroup)")
    public Object validateAround(ProceedingJoinPoint joinPoint, RequestMapping requestMapping, ValidateGroup validateGroup) throws Throwable {
        ValidateGroup an = null;
        Method method = null;
        Object target = null;
        String methodName = null;
        String paramName = "";
        try {
            methodName = joinPoint.getSignature().getName();
            target = joinPoint.getTarget();
            // 得到拦截的方法
            method = getMethodByClassAndName(target.getClass(), methodName);
            // 方法的参数
            args = joinPoint.getArgs();
            an = (ValidateGroup) getAnnotationByMethod(method, ValidateGroup.class);
            for (ValidateField validateField : an.fields()) {
                paramName = validateField.paramName();
                Object arg;
                if ("".equals(validateField.filedName())) {
                    arg = args[validateField.index()];
                } else {
                    arg = getFieldByObjectAndFileName(args[validateField.index()],
                            validateField.filedName());
                }

                // encodeXSS防控
                if (validateField.encodeXSS()) {
                    // 字符串才过滤特殊字符
                    if (arg instanceof String) {
                        arg = encodeXSS(String.valueOf(arg));
                        // 重新赋值
                        if ("".equals(validateField.filedName())) {
                            args[validateField.index()] = arg;
                        } else {
                            setFieldByObjectAndFileName(args[validateField.index()],
                                    validateField.filedName(), String.valueOf(arg));
                        }
                    }
                }

                // 判断参数是否为空
                if (validateField.notNull()) {
                    if (arg == null) {
                        throw new ParamException("【" + paramName + "】为空");
                    }
                    if (arg instanceof String) {
                        if (String.valueOf(arg) == "") {
                            throw new ParamException("【" + paramName + "】为空");
                        }
                    }
                }

                // 判断字符串最大长度
                if (validateField.maxLen() > 0) {
                    if (arg instanceof String) {
                        if (String.valueOf(arg).length() > validateField.maxLen()) {
                            throw new ParamException("【" + paramName + "】长度不能大于" + validateField.maxLen());
                        }
                    }
                }

                // 判断字符串最小长度
                if (validateField.minLen() > 0) {
                    if (arg instanceof String) {
                        if (String.valueOf(arg).length() < validateField.minLen()) {
                            throw new ParamException("【" + paramName + "】长度不能小于" + validateField.minLen());
                        }
                    }
                }

                // 判断数值最大值
                if (!"".equals(validateField.maxVal())) {
                    // 参数转换BigDecimal
                    BigDecimal param = getBigDecimal(arg);
                    // 验证值转换BigDecimal
                    BigDecimal maxVal = getBigDecimal(validateField.maxVal());
                    if (param.compareTo(maxVal) == 1) {
                        throw new ParamException("【" + paramName + "】已超过最大值");
                    }
                }

                // 判断数值最小值
                if (!"".equals(validateField.minVal())) {
                    // 参数转换BigDecimal
                    BigDecimal param = getBigDecimal(arg);
                    // 验证值转换BigDecimal
                    BigDecimal minVal = getBigDecimal(validateField.minVal());
                    if (param.compareTo(minVal) == -1) {
                        throw new ParamException("【" + paramName + "】已低于最小值");
                    }
                }

                // 判断正则
                if (!"".equals(validateField.regStr())) {
                    if (arg instanceof String) {
                        if (!((String) arg).matches(validateField.regStr())) {
                            // 正则验证，允许用户自定义错误信息
                            if (!"".equals(validateField.regErrorMsg())) {
                                throw new ParamException(validateField.regErrorMsg());
                            }
                            throw new ParamException("【" + paramName + "】格式错误");
                        }
                    } else {
                        throw new ParamException("【" + paramName + "】数据类型错误");
                    }
                }
            }
        } catch (Exception e) {
            this.error(method, e.getMessage());
        }
        return joinPoint.proceed(args);
    }

    /**
     * 错误返回， 必须重写
     *
     * @param method
     * @param errorMsg
     * @return
     * @throws Exception
     */
    public abstract Object error(Method method, String errorMsg) throws Exception;

    /**
     * 参数转换BigDecimal
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimal(Object value) throws ParamException {
        BigDecimal ret = null;
        try {
            if (value != null) {
                if (value instanceof BigDecimal) {
                    ret = (BigDecimal) value;
                } else {
                    ret = new BigDecimal(String.valueOf(value));
                }
            }
        } catch (Exception e) {
            throw new ParamException("参数转换错误");
        }
        return ret;
    }

    /**
     * 根据对象和属性名得到 属性
     */
    public Object getFieldByObjectAndFileName(Object targetObj, String fileName) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        String tmp[] = fileName.split("\\.");
        Object arg = targetObj;
        Method method = targetObj.getClass().
                getMethod(getGetterNameByFiledName(tmp[1]));
        arg = method.invoke(arg);
        return arg;
    }

    /**
     * 根据对象和属性名重新设置值
     */
    public void setFieldByObjectAndFileName(Object targetObj, String fileName, String newValue) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        String tmp[] = fileName.split("\\.");
        Method method = targetObj.getClass().
                getMethod(getSetterNameByFiledName(tmp[1]), String.class);
        method.invoke(targetObj, newValue);
    }

    /**
     * 根据属性名 得到该属性的getter方法名
     */
    public String getGetterNameByFiledName(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    /**
     * 根据属性名 得到该属性的setter方法名
     */
    public String getSetterNameByFiledName(String fieldName) {
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    /**
     * 根据目标方法和注解类型  得到该目标方法的指定注解
     */
    public Annotation getAnnotationByMethod(Method method, Class annoClass) {
        Annotation all[] = method.getAnnotations();
        for (Annotation annotation : all) {
            if (annotation.annotationType() == annoClass) {
                return annotation;
            }
        }
        return null;
    }

    /**
     * 根据类和方法名得到方法
     */
    public Method getMethodByClassAndName(Class c, String methodName) {
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

    /**
     * 将容易引起xss漏洞的半角字符直接替换成全角字符
     *
     * @param param
     * @return
     */
    private String encodeXSS(String param) {
        if (param == null || "".equals(param)) {
            return param;
        }
        StringBuilder sb = new StringBuilder(param.length() + 16);
        for (int i = 0; i < param.length(); i++) {
            char c = param.charAt(i);
            switch (c) {
                case '>':
                    sb.append('＞');
                    break;
                case '<':
                    sb.append('＜');
                    break;
                case '\'':
                    sb.append('‘');
                    break;
                case '\"':
                    sb.append('“');
                    break;
                case '&':
                    sb.append('＆');
                    break;
                case '\\':
                    sb.append('＼');
                    break;
                case '#':
                    sb.append('＃');
                    break;
                case ' ':
                    sb.append("");
                    break;
                default:
                    sb.append(c);
                    break;
            }
        }
        return sb.toString();
    }
}
